//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Object types stuff.
//

# include "StdAfx.h"
# include "Resource.h"
# include "TypesDraw.h"
# include "TypesPOIDraw.h"
# include "Map.h"
# include "Position.h"
# include "Levels.h"
# include "PrintUnits.h"
# include "Settings.h"
# include "ParseHelpers.h"
# include "DDBitmap.h"
# include "Str.h"
# include "fast_floor.h"
# include "Globals.h"

HCURSOR g_hcurWaypoint;
int g_nCursorShiftX_Waypoint, g_nCursorShiftY_Waypoint;

HPEN g_hpenSelection;
HBRUSH g_hbrSelection;
HBRUSH g_hbrSelectionTransp;

HPEN g_hpenUnknown;
HBRUSH g_hbrUnknown;

HFONT g_hBigFont;
HFONT g_hMediumFont;
HFONT g_hRotatedFont [181];

HBRUSH g_hbrWater;
HPEN g_hpenWater;
HPEN g_hpenWideWater;
HBRUSH g_hbrDryWater;
HPEN g_hpenDryWater;
HPEN g_hpenCoast;
HBRUSH g_hbrGlacier;

HBRUSH g_hbrLargeUrbanHousing;
HBRUSH g_hbrSmallUrbanHousing;
HBRUSH g_hbrRuralHousing;
HBRUSH g_hbrIndustrial;
HBRUSH g_hbrUtilities;
HBRUSH g_hbrResidentialBuilding;
HBRUSH g_hbrOfficeBuilding;
HBRUSH g_hbrCommonBuilding;
HBRUSH g_hbrIndustrialBuilding;
HBRUSH g_hbrManMade;
HBRUSH g_hbrArterial;
HBRUSH g_hbrSquare;
HBRUSH g_hbrParkingLot;

HBRUSH g_hbrReservation;
HBRUSH g_hbrGolf;
HBRUSH g_hbrSwamp;
HBRUSH g_hbrForest;
HBRUSH g_hbrOrchard;
HBRUSH g_hbrScrub;
HBRUSH g_hbrTundra;
HBRUSH g_hbrSandFlat;
HBRUSH g_hbrCemetery;
HBRUSH g_hbrCityPark;

HBRUSH g_hbrForestSwamp;
HBRUSH g_hbrForestUndersized;
HBRUSH g_hbrForestSparse;
HBRUSH g_hbrForestDead;
HBRUSH g_hbrForestStumped;
HBRUSH g_hbrVineyard;
HBRUSH g_hbrRiceField;
HBRUSH g_hbrMeadow;
HBRUSH g_hbrSand;
HBRUSH g_hbrStony;
HBRUSH g_hbrSaline;
HBRUSH g_hbrReed;
HBRUSH g_hbrAlgae;
HBRUSH g_hbrThornbush;
HBRUSH g_hbrWindFallenTrees;
HBRUSH g_hbrElfinWood;
HBRUSH g_hbrSaxaul;
HBRUSH g_hbrBamboo;
HBRUSH g_hbrMangroves;
HBRUSH g_hbrPalmForest;
HBRUSH g_hbrHighGrass;
HBRUSH g_hbrMosses;
HBRUSH g_hbrLichens;

HPEN g_hpenHighway1;
HPEN g_hpenHighway2;
HPEN g_hpenPrincipalHw;
HPEN g_hpenOtherHw;
HPEN g_hpenArterialRoad;
HPEN g_hpenCollectorRoad;
HPEN g_hpenThinRoad;
HPEN g_hpenAlleyway;
HPEN g_hpenTunnelRoad;
HPEN g_hpenEarthRoad;
HPEN g_hpenUnpavedRoad;
HPEN g_hpenTrail;
HPEN g_hpenPlannedRoute;
HPEN g_hpenTrafficLane;
HPEN g_hpenForestFirebreak;

HPEN g_hpenRailroad1;
HPEN g_hpenRailroad2;
HPEN g_hpenFerry;
HPEN g_hpenTramWay;

HPEN g_hpenPipeLine;
HPEN g_hpenPowerLine;

HPEN g_hpenBoundaryInt1;
HPEN g_hpenBoundaryInt2;
HPEN g_hpenBoundaryReg1;
HPEN g_hpenBoundaryReg2;
HPEN g_hpenBoundaryCnt1;
HPEN g_hpenBoundaryCnt2;
HPEN g_hpenTimeZone;
HPEN g_hpenFence;

HPEN g_hpenHeight1;
HPEN g_hpenHeight2;
HPEN g_hpenHeight3;
HPEN g_hpenDepth1;
HPEN g_hpenDepth2;
HPEN g_hpenDepth3;

const COLORREF c_colFerry               = c_colCyan;
const COLORREF c_colWater               = RGB (0, 128, 255);
const COLORREF c_colWater2              = RGB (0,   0, 128);
const COLORREF c_colGlacier             = c_colCyan;
const COLORREF c_colSandFlat            = RGB (188, 237, 250);
const COLORREF c_colForest              = RGB (183/*(153+213)/2*/, 233/*(226+240)/2*/, 153/*(153+153)/2*/);
const COLORREF c_colForestUndersized    = RGB (211, 245, 165);
const COLORREF c_colCityPark            = RGB (144, 192, 0);
const COLORREF c_colReservation         = RGB (255, 210, 150);
const COLORREF c_colGolf                = c_colGreen;

const COLORREF c_colHeight              = RGB (182, 120, 36);//RGB (137, 80, 24);
const COLORREF c_colDepth               = c_colCyan;
const COLORREF c_colBoundary            = RGB (0, 200, 100);

const COLORREF c_colLargeUrbanHousing   = RGB (210, 192, 192); // c_colLtGray;
const COLORREF c_colSmallUrbanHousing   = RGB (192, 192, 192); // c_colLtGray;
const COLORREF c_colRuralHousing        = RGB (164, 176, 148); // c_colLtGray;
const COLORREF c_colIndustrial          = RGB (128, 128, 128); // c_colGray;
const COLORREF c_colUtilities           = RGB (248, 184, 128);
const COLORREF c_colResidentialBuilding = RGB (0xfe, 0x87, 0x48);
const COLORREF c_colOfficeBuilding      = RGB (160, 200, 240);
const COLORREF c_colCommonBuilding      = RGB (240, 160, 240);
const COLORREF c_colIndustrialBuilding  = RGB (150, 150, 150);
const COLORREF c_colManMade             = RGB (204, 153,   0); //RGB (150, 150, 150);
const COLORREF c_colArterial            = RGB (0xF9, 0xBA, 0x3E);
const COLORREF c_colSquare              = RGB (224, 224, 224); // c_colLtGray;
const COLORREF c_colParkingLot          = RGB (240, 240, 240);

static
HBRUSH _CreatePatternBrush (UINT _uiPattern) {
	const HBITMAP hbmp = ::LoadBitmap (g_hInst, MAKEINTRESOURCE (_uiPattern));
	CBitmapHolder hbmp_Holder (hbmp);

	return ::CreatePatternBrush (hbmp);
}

static
HBRUSH _CreatePatternBrush (UINT _uiPattern, COLORREF _colBg) {
	const HBRUSH hbrPattern = _CreatePatternBrush (_uiPattern);
	CGdiObjectHolder hbrPattern_Holder (hbrPattern);

	const size_t c_w = 16, c_h = 16;
	const RECT rect = {0, 0, c_w, c_h};

	CDDBitmap ddbPattern (c_w, c_h);
	::FillRect (ddbPattern.GetDC (), & rect, hbrPattern);

	const HBRUSH hbrColor = ::CreateSolidBrush (_colBg);
	CGdiObjectHolder hbrColor_Holder (hbrColor);

	CDDBitmap ddb (c_w, c_h);
	::FillRect (ddb.GetDC (), & rect, hbrColor);

	::BitBlt (ddb.GetDC (), 0, 0, c_w, c_h, ddbPattern.GetDC (), 0, 0, SRCAND);

	return ::CreatePatternBrush (ddb.GetBitmap ());
}

void InitCommonTypesGDI () {
	//
	// Prepare fonts for labels.
	//

	g_hBigFont    = ::CreateFont (18, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Sans Serif");
	g_hMediumFont = ::CreateFont (14, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Sans Serif");

	for (int nAngle = -90; nAngle <= 90; ++ nAngle)
		g_hRotatedFont [nAngle + 90] = ::CreateFont (12, 0, nAngle*10, nAngle*10, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Sans Serif");

	//
	// Prepare pens & brushes for polylines & polygons.
	//

	g_hpenSelection         = ::CreatePen (PS_DOT, 1, RGB (0, 0, 0));
	g_hbrSelection          = _CreatePatternBrush (IDB_SelectPattern);
	g_hbrSelectionTransp    = _CreatePatternBrush (IDB_SelectPatternTransp);

	g_hpenUnknown           = ::CreatePen (PS_DOT, 1, c_colRed);
	g_hbrUnknown            = ::CreateHatchBrush (HS_BDIAGONAL, c_colRed);

	g_hbrWater              = ::CreateSolidBrush (c_colWater);
	g_hpenWater             = ::CreatePen (PS_SOLID, 1, c_colBlue);
	g_hpenWideWater         = ::CreatePen (PS_SOLID, 2, c_colBlue);
	g_hbrDryWater           = ::CreateHatchBrush (HS_DIAGCROSS, c_colWater);
	g_hpenDryWater          = ::CreatePen (PS_DASH,  1, c_colBlue);
	g_hpenCoast             = ::CreatePen (PS_SOLID, 1, c_colWater2);
	g_hbrGlacier            = ::CreateSolidBrush (c_colGlacier);

	g_hbrLargeUrbanHousing  = ::CreateSolidBrush (c_colLargeUrbanHousing);
	g_hbrSmallUrbanHousing  = ::CreateSolidBrush (c_colSmallUrbanHousing);
	g_hbrRuralHousing       = ::CreateSolidBrush (c_colRuralHousing);
	g_hbrIndustrial         = ::CreateSolidBrush (c_colIndustrial);
	g_hbrUtilities          = ::CreateSolidBrush (c_colUtilities);
	g_hbrResidentialBuilding= ::CreateSolidBrush (c_colResidentialBuilding);
	g_hbrOfficeBuilding     = ::CreateSolidBrush (c_colOfficeBuilding);
	g_hbrCommonBuilding     = ::CreateSolidBrush (c_colCommonBuilding);
	g_hbrIndustrialBuilding = ::CreateSolidBrush (c_colIndustrialBuilding);
	g_hbrManMade            = ::CreateSolidBrush (c_colManMade);
	g_hbrArterial           = ::CreateSolidBrush (c_colArterial);
	g_hbrSquare             = ::CreateSolidBrush (c_colSquare);
	g_hbrParkingLot         = ::CreateSolidBrush (c_colParkingLot);

	g_hbrReservation        = ::CreateSolidBrush (c_colReservation);
	g_hbrGolf               = ::CreateSolidBrush (c_colGolf);
	g_hbrSwamp              = _CreatePatternBrush (IDB_Swamp);//::CreateHatchBrush (HS_HORIZONTAL, c_colBlue);
	g_hbrForest             = ::CreateSolidBrush (c_colForest);
	g_hbrOrchard            = _CreatePatternBrush (IDB_Orchard);
	g_hbrScrub              = _CreatePatternBrush (IDB_Scrub, c_colForestUndersized);
	g_hbrTundra             = _CreatePatternBrush (IDB_Tundra);
	g_hbrSandFlat           = _CreatePatternBrush (IDB_Sand, c_colSandFlat);
	g_hbrCemetery           = _CreatePatternBrush (IDB_Cemetery);
	g_hbrCityPark           = ::CreateSolidBrush (c_colCityPark);

	g_hbrForestSwamp        = _CreatePatternBrush (IDB_Swamp, c_colForest);
	g_hbrForestUndersized   = ::CreateSolidBrush (c_colForestUndersized);
	g_hbrForestSparse       = _CreatePatternBrush (IDB_ForestSparse);
	g_hbrForestDead         = _CreatePatternBrush (IDB_ForestDead);
	g_hbrForestStumped      = _CreatePatternBrush (IDB_ForestStumped);
	g_hbrVineyard           = _CreatePatternBrush (IDB_Vineyard);
	g_hbrRiceField          = _CreatePatternBrush (IDB_RiceField);
	g_hbrMeadow             = _CreatePatternBrush (IDB_Meadow);
	g_hbrSand               = _CreatePatternBrush (IDB_Sand);
	g_hbrStony              = _CreatePatternBrush (IDB_Stony);
	g_hbrSaline             = ::CreateHatchBrush (HS_VERTICAL, c_colBlue);
	g_hbrReed               = _CreatePatternBrush (IDB_Reed);
	g_hbrAlgae              = _CreatePatternBrush (IDB_Algae,           c_colWater);
	g_hbrThornbush          = _CreatePatternBrush (IDB_Thornbush,       c_colForestUndersized);
	g_hbrWindFallenTrees    = _CreatePatternBrush (IDB_WindFallenTrees, c_colForest);
	g_hbrElfinWood          = _CreatePatternBrush (IDB_ElfinWood,       c_colForestUndersized);
	g_hbrSaxaul             = _CreatePatternBrush (IDB_Saxaul,          c_colForestUndersized);
	g_hbrBamboo             = _CreatePatternBrush (IDB_Bamboo,          c_colForestUndersized);
	g_hbrPalmForest         = _CreatePatternBrush (IDB_PalmForest,      c_colForest);
	g_hbrMangroves          = _CreatePatternBrush (IDB_Mangroves);
	g_hbrHighGrass          = _CreatePatternBrush (IDB_HighGrass);
	g_hbrMosses             = _CreatePatternBrush (IDB_Mosses);
	g_hbrLichens            = _CreatePatternBrush (IDB_Lichens);

	g_hpenHighway1          = ::CreatePen (PS_SOLID, 4, RGB (100,  50, 50));
	g_hpenHighway2          = ::CreatePen (PS_SOLID, 2, RGB (196, 100, 66));
	g_hpenPrincipalHw       = ::CreatePen (PS_SOLID, 3, RGB (100,  50, 50));
	g_hpenOtherHw           = ::CreatePen (PS_SOLID, 1, RGB (100,  50, 50));//196, 100, 66));
	g_hpenArterialRoad      = ::CreatePen (PS_SOLID, 3, c_colBlack);
	g_hpenCollectorRoad     = ::CreatePen (PS_SOLID, 2, c_colBlack);
	g_hpenThinRoad          = ::CreatePen (PS_SOLID, 1, c_colBlack);
	g_hpenAlleyway          = ::CreatePen (PS_SOLID, 1, RGB (0xC4, 0x64, 0x42));
	g_hpenTunnelRoad        = ::CreatePen (PS_DASH,  1, c_colBlack);
	g_hpenEarthRoad         = ::CreatePen (PS_SOLID, 1, c_colBlack);
	g_hpenUnpavedRoad       = ::CreatePen (PS_DASH,  1, c_colBlack);
	g_hpenTrail             = ::CreatePen (PS_DOT,   1, c_colBlack);
	g_hpenPlannedRoute      = ::CreatePen (PS_SOLID, 6, RGB (232, 0, 232));
	g_hpenTrafficLane       = ::CreatePen (PS_SOLID, 1, RGB (232, 0, 232));
	g_hpenForestFirebreak   = ::CreatePen (PS_DOT,   1, c_colGray);

	g_hpenRailroad1         = ::CreatePen (PS_SOLID, 3, c_colBlack);
	g_hpenRailroad2         = ::CreatePen (PS_DOT,   1, c_colWhite);
	g_hpenFerry             = ::CreatePen (PS_DASH,  1, c_colFerry);
	g_hpenTramWay           = ::CreatePen (PS_SOLID, 2, c_colBlack);

	g_hpenPipeLine          = ::CreatePen (PS_DOT,   1, c_colGray);
	g_hpenPowerLine         = ::CreatePen (PS_SOLID, 1, c_colGray);

	g_hpenBoundaryInt1      = ::CreatePen (PS_SOLID, 2, c_colRed);
	g_hpenBoundaryInt2      = ::CreatePen (PS_DOT,   1, c_colBoundary);
	g_hpenBoundaryReg1      = ::CreatePen (PS_SOLID, 1, c_colRed);
	g_hpenBoundaryReg2      = ::CreatePen (PS_DOT,   1, c_colBoundary);
	g_hpenBoundaryCnt1      = ::CreatePen (PS_SOLID, 1, c_colBlack);
	g_hpenBoundaryCnt2      = ::CreatePen (PS_DOT,   1, c_colBoundary);
	g_hpenTimeZone          = ::CreatePen (PS_SOLID, 1, c_colGreen);
	g_hpenFence             = ::CreatePen (PS_DOT,   1, c_colBoundary);

	g_hpenHeight1           = ::CreatePen (PS_DOT,   1, c_colHeight);
	g_hpenHeight2           = ::CreatePen (PS_SOLID, 1, c_colHeight);
	g_hpenHeight3           = ::CreatePen (PS_SOLID, 2, c_colHeight);
	g_hpenDepth1            = ::CreatePen (PS_DOT,   1, c_colDepth);
	g_hpenDepth2            = ::CreatePen (PS_SOLID, 1, c_colDepth);
	g_hpenDepth3            = ::CreatePen (PS_SOLID, 2, c_colDepth);

	//
	// Prepare the waypoint mark.
	//
	g_hcurWaypoint          = ::LoadCursor (g_hInst, MAKEINTRESOURCE (IDC_Waypoint));

	ICONINFO ii;
	::GetIconInfo (g_hcurWaypoint, & ii);
	g_nCursorShiftX_Waypoint = ii.xHotspot;
	g_nCursorShiftY_Waypoint = ii.yHotspot;
	::DeleteObject (ii.hbmColor);
	::DeleteObject (ii.hbmMask);

	InitCommonPOIs ();
}

void DestroyCommonTypesGDI () {
	::DeleteObject (g_hBigFont);
	::DeleteObject (g_hMediumFont);

	for (size_t c = 0; c < sizeof (g_hRotatedFont)/sizeof (g_hRotatedFont [0]); ++ c)
		::DeleteObject (g_hRotatedFont [c]);

	::DeleteObject (g_hpenSelection);
	::DeleteObject (g_hbrSelection);
	::DeleteObject (g_hbrSelectionTransp);

	::DeleteObject (g_hpenUnknown);
	::DeleteObject (g_hbrUnknown);

	::DeleteObject (g_hbrWater);
	::DeleteObject (g_hpenWater);
	::DeleteObject (g_hpenWideWater);
	::DeleteObject (g_hbrDryWater);
	::DeleteObject (g_hpenDryWater);
	::DeleteObject (g_hpenCoast);
	::DeleteObject (g_hbrGlacier);

	::DeleteObject (g_hbrLargeUrbanHousing);
	::DeleteObject (g_hbrSmallUrbanHousing);
	::DeleteObject (g_hbrRuralHousing);
	::DeleteObject (g_hbrIndustrial);
	::DeleteObject (g_hbrUtilities);
	::DeleteObject (g_hbrResidentialBuilding);
	::DeleteObject (g_hbrOfficeBuilding);
	::DeleteObject (g_hbrCommonBuilding);
	::DeleteObject (g_hbrIndustrialBuilding);
	::DeleteObject (g_hbrArterial);
	::DeleteObject (g_hbrSquare);
	::DeleteObject (g_hbrParkingLot);

	::DeleteObject (g_hbrReservation);
	::DeleteObject (g_hbrGolf);
	::DeleteObject (g_hbrSwamp);
	::DeleteObject (g_hbrForest);
	::DeleteObject (g_hbrOrchard);
  	::DeleteObject (g_hbrScrub);
	::DeleteObject (g_hbrTundra);
	::DeleteObject (g_hbrSandFlat);
	::DeleteObject (g_hbrCemetery);
	::DeleteObject (g_hbrCityPark);

	::DeleteObject (g_hbrForestSwamp);
	::DeleteObject (g_hbrForestUndersized);
	::DeleteObject (g_hbrForestSparse);
	::DeleteObject (g_hbrForestDead);
	::DeleteObject (g_hbrForestStumped);
	::DeleteObject (g_hbrVineyard);
	::DeleteObject (g_hbrRiceField);
	::DeleteObject (g_hbrMeadow);
	::DeleteObject (g_hbrSand);
	::DeleteObject (g_hbrStony);
	::DeleteObject (g_hbrSaline);
	::DeleteObject (g_hbrReed);
	::DeleteObject (g_hbrAlgae);
	::DeleteObject (g_hbrThornbush);
	::DeleteObject (g_hbrWindFallenTrees);
	::DeleteObject (g_hbrElfinWood);
	::DeleteObject (g_hbrSaxaul);
	::DeleteObject (g_hbrBamboo);
	::DeleteObject (g_hbrPalmForest);
	::DeleteObject (g_hbrMangroves);
	::DeleteObject (g_hbrHighGrass);
	::DeleteObject (g_hbrMosses);
	::DeleteObject (g_hbrLichens);

	::DeleteObject (g_hpenHighway1);
	::DeleteObject (g_hpenHighway2);
	::DeleteObject (g_hpenPrincipalHw);
	::DeleteObject (g_hpenOtherHw);
	::DeleteObject (g_hpenArterialRoad);
	::DeleteObject (g_hpenCollectorRoad);
	::DeleteObject (g_hpenThinRoad);
	::DeleteObject (g_hpenAlleyway);
	::DeleteObject (g_hpenTunnelRoad);
	::DeleteObject (g_hpenEarthRoad);
	::DeleteObject (g_hpenUnpavedRoad);
	::DeleteObject (g_hpenTrail);
	::DeleteObject (g_hpenPlannedRoute);
	::DeleteObject (g_hpenTrafficLane);

	::DeleteObject (g_hpenRailroad1);
	::DeleteObject (g_hpenRailroad2);
	::DeleteObject (g_hpenFerry);
	::DeleteObject (g_hpenTramWay);

	::DeleteObject (g_hpenPowerLine);
	::DeleteObject (g_hpenPipeLine);

	::DeleteObject (g_hpenBoundaryInt1);
	::DeleteObject (g_hpenBoundaryInt2);
	::DeleteObject (g_hpenBoundaryReg1);
	::DeleteObject (g_hpenBoundaryReg2);
	::DeleteObject (g_hpenBoundaryCnt1);
	::DeleteObject (g_hpenBoundaryCnt2);
	::DeleteObject (g_hpenTimeZone);
	::DeleteObject (g_hpenFence);

	::DeleteObject (g_hpenHeight1);
	::DeleteObject (g_hpenHeight2);
	::DeleteObject (g_hpenHeight3);
	::DeleteObject (g_hpenDepth1);
	::DeleteObject (g_hpenDepth2);
	::DeleteObject (g_hpenDepth3);
}

//////////////////////////////////////////////////

static
void _SetTextSize (rgn_t & _rgn) {
	if (_rgn.strLabel.empty ()) {
		_rgn.ucLabelWidthBy8 = 0;
		_rgn.ucLabelHeight = 0;
		return;
	}

	const char * str = _rgn.strLabel.c_str ();
	if (str [0] == '~') {
		// Discard highway pefix code.
		if (
			::strncmp (str + 1, "[0x2", 4) == 0 ||
			::strncmp (str + 1, "[0x0", 4) == 0
		)
			str = _rgn.strLabel.c_str () + 5;
	}

	if (_rgn.fSize2 != 0) {
		const CClientDC hdcScreen (NULL);

		const type_desc_t * const pTD = _rgn.GetTypeDesc ();
		assert (pTD);
		if (pTD)
			::SelectObject (hdcScreen, pTD->phFont ? (* pTD->phFont) : g_hFont);

		RECT rect;
		::memset (& rect, 0, sizeof (RECT));
		::DrawText (hdcScreen, str, -1, & rect, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);

		_rgn.ucLabelWidthBy8 = (rect.right - rect.left + 7)/8;
		_rgn.ucLabelHeight = rect.bottom - rect.top;
	} else {
		_rgn.ucLabelWidthBy8 = (::strlen (str)*6 + 7)/8;
		_rgn.ucLabelHeight = 14;
	}
}

///////////////////////////////////////////////

void PreparePointLabel (rgn_t & _rgn) {
	if (! _rgn.origins.empty ()) {
		_rgn.ptLabel = _rgn.origins [0].point;
		_rgn.dwFlags |= rgn_t::fPtLabelDefined;
		_SetTextSize (_rgn);
		return;
	}

	float ave_x = 0, ave_y = 0;
	size_t ave_N = 0;
	for (size_t cElement = 0; cElement < _rgn.elements.size (); ++ cElement) {
		const rgn_element_t & element = _rgn.elements [cElement];
		if (element.bDeleted)
			continue;

		const points_t & points = element.points;
		const size_t cPoints = points.size ();

		for (size_t cPoint = 0; cPoint < cPoints; ++ cPoint) {
			const point_t & pt = points [cPoint];
			ave_x += pt.x;
			ave_y += pt.y;
			++ ave_N;
		}
	}
	if (ave_N) {
		_rgn.ptLabel.x = ave_x/ave_N;
		_rgn.ptLabel.y = ave_y/ave_N;
		_rgn.chLabelAngle = 0;
		_rgn.dwFlags |= rgn_t::fPtLabelDefined;
	}

	_SetTextSize (_rgn);
}

static
signed char _GetLableAngle (float _dy, float _dx) {
	float fLabelAngle = ::atan2 (_dy, _dx);

	if (fLabelAngle >= c_PI/2)
		fLabelAngle -= c_PI;
	if (fLabelAngle < -c_PI/2)
		fLabelAngle += c_PI;
	assert (-c_PI/2 <= fLabelAngle && fLabelAngle < c_PI/2);

	const int nAngle = fast_floor (fLabelAngle/c_PI*180 + .5f);
	assert (-90 <= nAngle && nAngle <= 90);

	return nAngle;
}

static
void _PreparePolylineLabel (rgn_t & _rgn) {
	if (_rgn.elements.empty ())
		return;

	// Get middle element.
	const rgn_element_t * pElement = _rgn.GetNonDeletedElement (_rgn.GetElements ()/2);
	if (pElement == NULL)
		return;

	const points_t & points = pElement->points;
	const size_t cPoints = points.size ();
	if (cPoints == 0)
		return;

	const float fCosY = ::cos (points [0].y*c_PI/180.f);

	// Estimate its length.
	float part_l = 0;
	size_t cPoint;
	for (cPoint = 0; cPoint < cPoints - 1; ++ cPoint) {
		const point_t & p0 = points [cPoint];
		const point_t & p1 = points [cPoint + 1];

		part_l += ::sqrt ((p1.x - p0.x)*(p1.x - p0.x)*fCosY*fCosY + (p1.y - p0.y)*(p1.y - p0.y));
	}

	// Find the middle segment.
	float part_l2 = 0;
	for (cPoint = 0; cPoint < cPoints - 1; ++ cPoint) {
		const point_t & p0 = points [cPoint];
		const point_t & p1 = points [cPoint + 1];

		const float l = ::sqrt ((p1.x - p0.x)*(p1.x - p0.x)*fCosY*fCosY + (p1.y - p0.y)*(p1.y - p0.y));
		if (part_l2 + l > part_l/2) {
			_rgn.ptLabel.x = (p0.x + p1.x)/2;
			_rgn.ptLabel.y = (p0.y + p1.y)/2;
			_rgn.chLabelAngle = _GetLableAngle (p1.y - p0.y, (p1.x - p0.x)*fCosY);
			_rgn.dwFlags |= rgn_t::fPtLabelDefined;
			break;
		} else
			part_l2 += l;
	}

	_SetTextSize (_rgn);
}

static
void _PreparePolygonLabel (rgn_t & _rgn) {
	if (_rgn.elements.empty ())
		return;

	// Get some element of the object.
	const rgn_element_t * pElement = _rgn.GetNonDeletedElement (0);
	if (pElement == NULL)
		return;

	const points_t & points = pElement->points;
	const size_t cPoints = points.size ();
	if (cPoints == 0)
		return;

	const float fCosY = ::cos (points [0].y*c_PI/180.f);

	// Find first most distant point (relative to 0-th one).
	float fMaxD2 = 0;
	size_t cMostDistantPoint = 0;
	size_t cPoint;
	const point_t & pt0 = points [0];
	for (cPoint = 1; cPoint < cPoints; ++ cPoint) {
		const point_t & pt = points [cPoint];

		float d2 = (pt.x - pt0.x)*(pt.x - pt0.x)*fCosY*fCosY + (pt.y - pt0.y)*(pt.y - pt0.y);
		if (d2 > fMaxD2) {
			fMaxD2 = d2;
			cMostDistantPoint = cPoint;
		}
	}

	// Find second most distant point (relative to first found one).
	fMaxD2 = 0;
	size_t cMostDistantPoint2 = 0;
	const point_t & pt1 = points [cMostDistantPoint];
	for (cPoint = 1; cPoint < cPoints; ++ cPoint) {
		const point_t & pt = points [cPoint];

		const float d2 = (pt.x - pt1.x)*(pt.x - pt1.x)*fCosY*fCosY + (pt.y - pt1.y)*(pt.y - pt1.y);
		if (d2 > fMaxD2) {
			fMaxD2 = d2;
			cMostDistantPoint = cPoint;
		}
	}

	// Get middle segment of the range.
	cPoint = (cMostDistantPoint + cMostDistantPoint2)/2;
	const point_t & p0 = points [cPoint];
	const point_t & p1 = points [cPoint + 1];
	_rgn.ptLabel.x = (p0.x + p1.x)/2;
	_rgn.ptLabel.y = (p0.y + p1.y)/2;
	_rgn.chLabelAngle = _GetLableAngle (p1.y - p0.y, (p1.x - p0.x)*fCosY);
	_rgn.dwFlags |= rgn_t::fPtLabelDefined;

	_SetTextSize (_rgn);
}

/////////////////////////////////////////

static
void _DrawLabelImpl (HDC _hDC, const rgn_t & _rgn, const char * _strLabel, size_t _cLen) {
	assert (! _rgn.strLabel.empty ());

	bool bXYDefined = false;
	int x, y;

	if (_rgn.dwFlags & rgn_t::fPtLabelDefined) {
		x = XToScreen (_rgn.ptLabel.x);
		y = YToScreen (_rgn.ptLabel.y);
		bXYDefined = true;
	} else {
		for (size_t cOrigin = 0; cOrigin < _rgn.origins.size (); ++ cOrigin) {
			const rgn_origin_t & origin = _rgn.origins [cOrigin];
			if (origin.bDeleted)
				continue;

			if (! _rgn.IsWTR () && ! LevelIsVisible (origin.cLevel, _rgn.uchEndLevel))
				continue;

			x = XToScreen (origin.point.x);
			y = YToScreen (origin.point.y);
			bXYDefined = true;

			break;
		}
	}

	if (bXYDefined) {
		const type_desc_t * const pTD = _rgn.GetTypeDesc ();
		assert (pTD);
		if (pTD) {
			::SetTextColor (_hDC, pTD->colText);
			::SetTextAlign (_hDC, TA_TOP | TA_LEFT);
			::SetBkMode (_hDC, TRANSPARENT); 
			::SelectObject (_hDC, pTD->phFont ? * (pTD->phFont) : g_hFont);
			::TextOut (_hDC, x + 2, y + 2, _strLabel, _cLen);
		}
	}
}

void DrawLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawLabelImpl (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

static
void _DrawRotatedLabel (HDC _hDC, const rgn_t & _rgn, const char * _strLabel, size_t _cLabelLen) {
	assert (! _rgn.strLabel.empty ());
	if (! (_rgn.dwFlags & rgn_t::fPtLabelDefined))
		return;

	float fLabelAngle = _rgn.chLabelAngle*c_PI/180;
	assert (-c_PI/2 <= fLabelAngle && fLabelAngle <= c_PI/2);

	const int x = XToScreen (_rgn.ptLabel.x) - 3*::sin (fLabelAngle);
	const int y = YToScreen (_rgn.ptLabel.y) - 3*::cos (fLabelAngle);

	const int nAngle = _rgn.chLabelAngle;
	assert (-90 <= nAngle && nAngle <= 90);
	const HFONT hFont = g_hRotatedFont [nAngle + 90];

	// Draw the label.
	const type_desc_t * const pTD = _rgn.GetTypeDesc ();
	assert (pTD);
	if (pTD) {
		::SetTextColor (_hDC, pTD->colText);
		::SetTextAlign (_hDC, TA_BASELINE | TA_CENTER);
		::SetBkMode (_hDC, TRANSPARENT); 
		const HFONT hfontPrev = (HFONT) ::SelectObject (_hDC, hFont);
		::TextOut (_hDC, x + 2, y + 2, _strLabel, _cLabelLen);

		::SelectObject (_hDC, hfontPrev);
	}
}

/////////////////////////////////////////

static
void _DrawRegionLabel (HDC _hDC, const rgn_t & _rgn) {
	assert (! _rgn.strLabel.empty ());

	bool bXYDefined = false;
	int x, y;

	if (_rgn.dwFlags & rgn_t::fPtLabelDefined) {
		x = XToScreen (_rgn.ptLabel.x);
		y = YToScreen (_rgn.ptLabel.y);
		bXYDefined = true;
	} else {
		for (size_t cOrigin = 0; cOrigin < _rgn.origins.size (); ++ cOrigin) {
			const rgn_origin_t & origin = _rgn.origins [cOrigin];
			if (origin.bDeleted)
				continue;

			if (! _rgn.IsWTR () && ! LevelIsVisible (origin.cLevel, _rgn.uchEndLevel))
				continue;

			x = XToScreen (origin.point.x);
			y = YToScreen (origin.point.y);
			bXYDefined = true;

			break;
		}
	}

	if (bXYDefined) {
		const type_desc_t * const pTD = _rgn.GetTypeDesc ();
		assert (pTD);
		if (pTD) {
			::SetTextColor (_hDC, pTD->colText);
			::SetTextAlign (_hDC, TA_BASELINE | TA_CENTER);
			::SetBkMode (_hDC, TRANSPARENT); 
			::SelectObject (_hDC, pTD->phFont ? * (pTD->phFont) : g_hFont);
			::TextOut (_hDC, x + 2, y + 2, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
		}
	}
}

/////////////////////////////////////////

static
void _PrepareCenteredLabel (rgn_t & _rgn) {
	PreparePointLabel (_rgn);
}

static
void _DrawCenteredLabel (HDC _hDC, const rgn_t & _rgn) {
	assert (! _rgn.strLabel.empty ());

	const int x = XToScreen (_rgn.ptLabel.x);
	const int y = YToScreen (_rgn.ptLabel.y);

	const type_desc_t * const pTD = _rgn.GetTypeDesc ();
	assert (pTD);
	if (pTD) {
		::SetTextColor (_hDC, pTD->colText);
		::SetTextAlign (_hDC, TA_BASELINE | TA_CENTER);
		::SetBkMode (_hDC, TRANSPARENT); 
		::SelectObject (_hDC, pTD->phFont ? * (pTD->phFont) : g_hFont);
		::TextOut (_hDC, x + 2, y + 2, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
	}
}

/////////////////////////////////////////

static
void _PreparePolyRiverLabel (rgn_t & _rgn) {
	_PreparePolygonLabel (_rgn);
}

static
void _DrawPolyRiverLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawRotatedLabel (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

/////////////////////////////////////////

static
void _PrepareRiverLabel (rgn_t & _rgn) {
	_PreparePolylineLabel (_rgn);
}

static
void _DrawRiverLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawRotatedLabel (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

/////////////////////////////////////////

static
void _PrepareBoundaryLabel (rgn_t & _rgn) {
	_PreparePolylineLabel (_rgn);
}

static
void _DrawBoundaryLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawRotatedLabel (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

/////////////////////////////////////////

static
void _DrawElevationLabel (HDC _hDC, const rgn_t & _rgn, bool _bInteger) {
	assert (! _rgn.strLabel.empty ());

	float fElevation = 0;
	const char * p;
	if (ParseReal (_rgn.strLabel.c_str (), _rgn.strLabel.c_str () + _rgn.strLabel.size (), & p, fElevation)) {
		char strElevation [20];
		PrintElevation (strElevation, NULL, fElevation, g_map.Header.ElevationUnits, _bInteger);
		_DrawLabelImpl (_hDC, _rgn, strElevation, ::strlen (strElevation));
	} else
		_DrawLabelImpl (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

static
void _DrawDepthSpotLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawElevationLabel (_hDC, _rgn, false);
}

static
void _DrawElevSpotLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawElevationLabel (_hDC, _rgn, true);
}

static
void _DrawContourLabel (HDC _hDC, const rgn_t & _rgn, bool _bInteger) {
	assert (! _rgn.strLabel.empty ());

	float fElevation = 0;
	const char * p;
	if (ParseReal (_rgn.strLabel.c_str (), _rgn.strLabel.c_str () + _rgn.strLabel.size (), & p, fElevation)) {
		char strElevation [20];
		PrintElevation (strElevation, NULL, fElevation, g_map.Header.ElevationUnits, _bInteger);
		_DrawRotatedLabel (_hDC, _rgn, strElevation, ::strlen (strElevation));
	} else
		_DrawRotatedLabel (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
}

static
void _DrawContourLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawContourLabel (_hDC, _rgn, true);
}

static
void _DrawDepthLabel (HDC _hDC, const rgn_t & _rgn) {
	_DrawContourLabel (_hDC, _rgn, false);
}

/////////////////////////////////////////

static
void _DrawFeatureLabel (HDC _hDC, const rgn_t & _rgn) {
	assert (! _rgn.strLabel.empty ());

	const char * pPrefixEnd = ::strstr (_rgn.strLabel.c_str (), "~[0x1f]");
	if (pPrefixEnd == NULL) {
		_DrawLabelImpl (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
		return;
	}
	const char * pPrefix = _rgn.strLabel.c_str ();
	const char * pElevation = pPrefixEnd + 7;

	float fElevation = 0;
	const char * p;
	if (! ParseReal (pElevation, _rgn.strLabel.c_str () + _rgn.strLabel.size (), & p, fElevation)) {
		_DrawLabelImpl (_hDC, _rgn, _rgn.strLabel.c_str (), _rgn.strLabel.size ());
		return;
	}
	char strElevation [20];
	PrintElevation (strElevation, NULL, fElevation, g_map.Header.ElevationUnits, true);

	const size_t cPrefixLen = pPrefixEnd - pPrefix;
	if (cPrefixLen == 0) {
		_DrawLabelImpl (_hDC, _rgn, strElevation, ::strlen (strElevation));
		return;
	}

	char * const strDisplay = reinterpret_cast<char *> (alloca (_rgn.strLabel.size () + 10));
	::strncpy (strDisplay, pPrefix, cPrefixLen);
	strDisplay [cPrefixLen] = ' ';
	::strcpy (strDisplay + cPrefixLen + 1, strElevation);

	_DrawLabelImpl (_hDC, _rgn, strDisplay, ::strlen (strDisplay));
}

/////////////////////////////////////////

static
void _PrepareRoadLabel (rgn_t & _rgn) {
	_PreparePolylineLabel (_rgn);
}

static
void _DrawRoadLabel (HDC _hDC, const rgn_t & _rgn) {
	assert (! _rgn.strLabel.empty ());
	if (! (_rgn.dwFlags & rgn_t::fPtLabelDefined))
		return;

	bool bDrawInRect = false;
	const char * strLabel = _rgn.strLabel.c_str ();
	if (
		strLabel [0] == '~' &&
		strLabel [1] == '[' &&
		strLabel [2] == '0' &&
		(strLabel [3] == 'x' || strLabel [3] == 'X')
	) {
		char * pEnd = NULL;
		const int iCode = ::strtoul (strLabel + 4, & pEnd, 16);
		if (0x2a <= iCode && iCode <= 0x2f)      // for 6-bit coding
			bDrawInRect = true;
		else if (0x01 <= iCode && iCode <= 0x06) // for 8-bit coding
			bDrawInRect = true;

		const char * const p = lstrchr (strLabel + 4, ']');
		if (p && p [1])
			strLabel = p + 1;
	}

	if (! bDrawInRect) {
		_DrawRotatedLabel (_hDC, _rgn, strLabel, ::strlen (strLabel));
		return;
	}
	
	// TODO: 
	//    ~[0x2a] - Interstate highway
	//    ~[0x2b] - Highway - shield
	//    ~[0x2c] - Highway - round symbol
	//    ~[0x2d] - Highway - small symbol - blue/red
	//    ~[0x2e] - Highway - big symbol
	//    ~[0x2f] - Highway - small symbol - white

	::SetBkMode (_hDC, TRANSPARENT);
	::SetTextColor (_hDC, c_colBlack);
	::SetTextAlign (_hDC, TA_TOP | TA_LEFT);
	::SelectObject (_hDC, g_hFont);

	RECT rect;
	memset (& rect, 0, sizeof (RECT));
	::DrawText (_hDC, strLabel, -1, & rect, DT_CALCRECT | DT_NOPREFIX | DT_SINGLELINE);

	const int w = rect.right  - rect.left + 4;
	const int h = rect.bottom - rect.top;

	const int x = XToScreen (_rgn.ptLabel.x);
	const int y = YToScreen (_rgn.ptLabel.y);

	rect.left   = x - w/2;
	rect.right  = x + w/2;
	rect.top    = y - h/2;
	rect.bottom = y + h/2;

	::FillRect  (_hDC, & rect, g_hbrWhite);
	::FrameRect (_hDC, & rect, g_hbrBlack);
	::DrawText  (_hDC, strLabel, -1, & rect, DT_CENTER | DT_VCENTER | DT_NOPREFIX | DT_SINGLELINE);
}

///////////////////////////////////////////////

label_desc_t g_DefLabel       = {& g_hFont,       c_colBlack,    PreparePointLabel,      DrawLabel};

	// For points.
label_desc_t g_BigCityLabel   = {& g_hBigFont,    c_colBlack,    PreparePointLabel,      DrawLabel};
label_desc_t g_CityLabel      = {& g_hMediumFont, c_colBlack,    PreparePointLabel,      DrawLabel};

label_desc_t g_BigRegionLabel = {& g_hBigFont,    c_colBlack,    PreparePointLabel,      _DrawRegionLabel};
label_desc_t g_RegionLabel    = {& g_hMediumFont, c_colBlack,    PreparePointLabel,      _DrawRegionLabel};

label_desc_t g_DepthSpotLabel = {& g_hFont,       c_colHeight,   PreparePointLabel,      _DrawDepthSpotLabel};
label_desc_t g_ElevSpotLabel  = {& g_hFont,       c_colHeight,   PreparePointLabel,      _DrawElevSpotLabel};

label_desc_t g_FeatureLabel   = {& g_hFont,       c_colHeight,   PreparePointLabel,      _DrawFeatureLabel};

	// For polylines.
label_desc_t g_RoadLabel      = {& g_hFont,       c_colDkGray,   _PrepareRoadLabel,      _DrawRoadLabel};
label_desc_t g_LineRiverLabel = {& g_hFont,       c_colBlue,     _PrepareRiverLabel,     _DrawRiverLabel};
label_desc_t g_BoundaryLabel  = {& g_hFont,       c_colBoundary, _PrepareBoundaryLabel,  _DrawBoundaryLabel};
label_desc_t g_ContourLabel   = {& g_hFont,       c_colHeight,   _PreparePolylineLabel,  _DrawContourLabel};
label_desc_t g_DepthLabel     = {& g_hFont,       c_colDepth,    _PreparePolylineLabel,  _DrawDepthLabel};

	// For polygons.
label_desc_t g_CenteredLabel  = {& g_hFont,       c_colBlack,    _PrepareCenteredLabel,  _DrawCenteredLabel};
label_desc_t g_LakeLabel      = {& g_hFont,       c_colWater2,   _PrepareCenteredLabel,  _DrawCenteredLabel};
label_desc_t g_RiverLabel     = {& g_hFont,       c_colWater2,   _PreparePolyRiverLabel, _DrawPolyRiverLabel};
